package de.slothsoft.commons.swing.util;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.swing.DefaultListSelectionModel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

import de.slothsoft.commons.swing.AbstractListTableModel;
import de.slothsoft.commons.swing.DisplayableTableRowSorter;
import de.slothsoft.commons.swing.SlothsoftTableCellRenderer;

public class JTableUtil {

	// TODO: API for jtable: empty row / count / sum / average

	public static final String COMMAND_MOUSE_CLICK = "mouseClick";
	public static final String COMMAND_KEY_PRESSED = "keyPressed";

	/**
	 * Creates a table with a model.Uses the
	 * <code>DisplayableTableCellRenderer</code> on default.
	 * 
	 * @param model
	 *            - a model
	 * @return the newly created table
	 */

	public static JTable createTable(TableModel model) {
		return createTable(model, null);
	}

	/**
	 * Creates a table with a model and specific widths for it's columns. Uses
	 * the <code>DisplayableTableCellRenderer</code> on default.
	 * 
	 * @param model
	 *            - a model
	 * @param columnWidths
	 *            - the columns' widths
	 * @return the newly created table
	 */

	public static JTable createTable(TableModel model, int[] columnWidths) {
		return createTable(model, columnWidths,
				new SlothsoftTableCellRenderer());
	}

	/**
	 * Creates a table with a model, specific widths for it's columns and a
	 * table cell renderer.
	 * 
	 * @param model
	 *            - a model
	 * @param columnWidths
	 *            - the columns' widths
	 * @param renderer
	 *            - the renderer
	 * @return the newly created table
	 */

	public static <M extends TableModel> JTable createTable(M model,
			int[] columnWidths, TableCellRenderer renderer) {

		JTable table = new JTable();
		table.setModel(model);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		createRenderer(table, columnWidths, renderer);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
		table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
		table.setRowSorter(new DisplayableTableRowSorter<M>(model));

		return table;
	}

	/**
	 * Creates a renderer for each column of the table. Uses the
	 * <code>DisplayableTableCellRenderer</code> on default.
	 * 
	 * @param table
	 *            - the table
	 */

	public static void createRenderer(JTable table) {
		createRenderer(table, null);
	}

	/**
	 * Creates a renderer for each column of the table and sets the width of
	 * each table. Uses the <code>DisplayableTableCellRenderer</code> on
	 * default.
	 * 
	 * @param table
	 *            - the table
	 * @param columnWidths
	 *            - the columns' widths
	 */

	public static void createRenderer(JTable table, int[] columnWidths) {
		createRenderer(table, columnWidths, new SlothsoftTableCellRenderer());
	}

	/**
	 * Creates a renderer for each column of the table.
	 * 
	 * @param table
	 *            - the table
	 * @param columnWidths
	 *            - the columns' widths
	 * @param renderer
	 *            - the renderer
	 */

	public static void createRenderer(JTable table, int[] columnWidths,
			TableCellRenderer renderer) {

		Enumeration<TableColumn> columns = table.getColumnModel().getColumns();
		int columnNumber = 0;
		while (columns.hasMoreElements()) {
			TableColumn column = columns.nextElement();
			column.setCellRenderer(renderer);

			if (columnWidths != null) {
				setFixedColumnSize(column, columnWidths[columnNumber]);
			}

			column.setMinWidth(0);
			column.setMaxWidth(1000);
			columnNumber++;
		}
	}

	/**
	 * Sets the size of a column to a fixed, not changeable value
	 * 
	 * @param column
	 *            - the column
	 * @param columnSize
	 *            - the size of the column
	 */

	public static void setFixedColumnSize(TableColumn column, int columnSize) {
		column.setWidth(columnSize);
		column.setMinWidth(columnSize);
		column.setMaxWidth(columnSize);
	}

	/**
	 * Adds a selection listener to a table - e.g. a mouse listener and a key
	 * listener.
	 * 
	 * @param table
	 *            - the table to be added to
	 * @param listener
	 *            - the listener to be added
	 */

	public static void addSelectionListener(final JTable table,
			final ActionListener listener) {
		if (listener == null) {
			return;
		}
		table.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				ActionEvent event = new ActionEvent(e.getSource(), e.getID(),
						COMMAND_MOUSE_CLICK);
				listener.actionPerformed(event);
			}
		});
		table.addKeyListener(new KeyAdapter() {
			@Override
			public void keyTyped(KeyEvent e) {
				ActionEvent event = new ActionEvent(e.getSource(), e.getID(),
						COMMAND_KEY_PRESSED);
				listener.actionPerformed(event);
			}
		});
	}

	/**
	 * Returns the selected element of a JTable.
	 * 
	 * @return one element or null
	 */

	public static <T> T getSelectedElement(JTable table,
			AbstractListTableModel<T> model) {
		int selected = table.getSelectedRow();
		if (selected != -1) {
			int modelIndex = table.getRowSorter().convertRowIndexToModel(
					selected);
			return model.getList().get(modelIndex);
		}
		return null;
	}

	/**
	 * Returns the selected elements of a JTable.
	 * 
	 * @return a possible empty list
	 */

	public static <T> List<T> getSelectedElements(JTable table,
			AbstractListTableModel<T> model) {
		List<T> result = new ArrayList<T>(table.getSelectedRowCount());
		int[] selected = table.getSelectedRows();
		for (int s : selected) {
			int modelIndex = table.getRowSorter().convertRowIndexToModel(s);
			result.add(model.getList().get(modelIndex));
		}
		return result;
	}

	private JTableUtil() {
		// RUN YOU FOOLS!!!
	}
}
